package com.aleyn.mvvm.base

import android.app.Application
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.viewModelScope
import com.aleyn.mvvm.app.MVVMLin
import com.aleyn.mvvm.event.Message
import com.aleyn.mvvm.event.SingleLiveEvent
import com.aleyn.mvvm.network.ResponseThrowable
import com.aleyn.mvvm.network.UploadTransform
import com.blankj.utilcode.util.Utils
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import java.io.File

/**
 *   @auther : Aleyn
 *   time   : 2019/11/01
 */
open class BaseViewModel(
    application: Application = Utils.getApp(),
) : AndroidViewModel(application), LifecycleObserver {

    val defUI: UIChange by lazy { UIChange() }

    /**
     * 所有网络请求都在 viewModelScope 域中启动，当页面销毁时会自动
     * 调用ViewModel的  #onCleared 方法取消所有协程
     */
    fun launchUI(block: suspend CoroutineScope.() -> Unit) = viewModelScope.launch { block() }

    /**
     * 用流的方式进行网络请求
     */
    fun <T> launchFlow(block: suspend () -> T): Flow<T> {
        return flow {
            emit(block())
        }
    }

    /**
     *  不过滤请求结果
     * @param block 请求体
     * @param error 失败回调
     * @param complete  完成回调（无论成功失败都会调用）
     * @param isShowDialog 是否显示加载框
     */
    fun launchGo(
        block: suspend CoroutineScope.() -> Unit,
        error: suspend CoroutineScope.(ResponseThrowable) -> Unit = {
            defUI.toastEvent.postValue("${it.code}I${it.errMsg}")
        },
        complete: suspend CoroutineScope.() -> Unit = {},
        isShowDialog: Boolean = false,
    ) {
        val networkConnected = isNetworkConnected(getApplication())
        defUI.netEvent.postValue(networkConnected)
        if (isShowDialog) defUI.showDialog.call()
        launchUI {
            handleException(
                withContext(Dispatchers.IO) { block },
                { error(it) },
                {
                    defUI.dismissDialog.call()
                    complete()
                }
            )
        }
    }

    /**
     * 过滤请求结果，其他全抛异常
     * @param block 请求体
     * @param success 成功回调
     * @param error 失败回调
     * @param complete  完成回调（无论成功失败都会调用）
     * @param isShowDialog 是否显示加载框
     */
    fun <T> launchOnlyresult(
        block: suspend CoroutineScope.() -> IBaseResponse<T>,
        success: (T) -> Unit,
        error: (ResponseThrowable) -> Unit = {
            defUI.toastEvent.postValue("${it.code}I${it.errMsg}")
        },
        complete: () -> Unit = {},
        isShowDialog: Boolean = true,
    ) {
        if (isShowDialog) defUI.showDialog.call()
        launchUI {
            handleException(
                {
                    withContext(Dispatchers.IO) {
                        block().let {
                            if (it.isSuccess()) it.data()
                            else throw ResponseThrowable(it.code(), it.msg())
                        }
                    }.also { success(it) }
                },
                { error(it) },
                {
                    defUI.dismissDialog.call()
                    complete()
                }
            )
        }
    }


    /**
     * 异常统一处理
     */
    private suspend fun handleException(
        block: suspend CoroutineScope.() -> Unit,
        error: suspend CoroutineScope.(ResponseThrowable) -> Unit,
        complete: suspend CoroutineScope.() -> Unit,
    ) {
        coroutineScope {
            try {
                block()
            } catch (e: Throwable) {
                error(MVVMLin.getConfig().globalExceptionHandle(e))
            } finally {
                complete()
            }
        }
    }


    /**
     * UI事件
     */
    inner class UIChange {
        val showDialog by lazy { SingleLiveEvent<String>() }
        val dismissDialog by lazy { SingleLiveEvent<Void>() }
        val toastEvent by lazy { SingleLiveEvent<String>() }
        val msgEvent by lazy { SingleLiveEvent<Message>() }
        val netEvent by lazy { SingleLiveEvent<Boolean>() }
    }

    fun getPart(path: String): MultipartBody.Part? {
        if (!path.isEmpty()) {
            val part = UploadTransform.getPart(path)
//            val part = UploadTransform.getPart(syncFromUri(Uri.parse(path)))
            return part
        }
        return null
    }

    fun getPart(name: String, path: String): MultipartBody.Part? {
        if (!path.isEmpty()) {
//            val part = UploadTransform.getPart(name, path)

//            val part = UploadTransform.getPart(name, syncFromUri(Uri.parse(path)))

            val file = File(path)
            val requestBody: RequestBody =
                RequestBody.create("multipart/form-data".toMediaTypeOrNull(),file)
            val part = MultipartBody.Part.createFormData(name, file.name, requestBody)
            return part
        }
        return null
    }

//    fun syncFromUri(uri: Uri): String? {
//        val options = FileCompressOptions()
//        val fileResult = getInstance().source(uri.path)
//            .asFile().withOptions(options).compressSync()
//        return fileResult.outfile
//    }

    //    open fun isNetworkConnected(context: Context?): Boolean {
//        if (context != null) {
//            val mConnectivityManager: ConnectivityManager = context
//                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
//            val mNetworkInfo: NetworkInfo = mConnectivityManager.getActiveNetworkInfo()!!
//            if (mNetworkInfo != null) {
//                return mNetworkInfo.isAvailable()
//            }
//        }
//        return false
//    }
    open fun isNetworkConnected(context: Context?): Boolean {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            return isNetSystemUsable(context)
        } else {
            return isNetwork(context)
        }
        return false
    }

    open fun isNetwork(context: Context?): Boolean {
        if (context != null) {
            val mConnectivityManager = context
                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val mNetworkInfo = mConnectivityManager.activeNetworkInfo
            if (mNetworkInfo != null) {
                return mNetworkInfo.isAvailable
            }
        }
        return false
    }

    @RequiresApi(Build.VERSION_CODES.M)
    open fun isNetSystemUsable(context: Context?): Boolean {
        var isNetUsable = false
        val manager = context?.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val networkCapabilities: NetworkCapabilities? =
            manager.getNetworkCapabilities(manager.activeNetwork)
        if (networkCapabilities != null) {
            isNetUsable =
                networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
        }
        return isNetUsable
    }
}